package com.uxwine.widget;

import com.uxwine.util.UxToast;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;

public class GestureListView extends ListView
{
	private final float mAlpha = 1.0f;
	private ImageView mDragView;
	private Context mContext;
	private WindowManager mWindowManager;
	private WindowManager.LayoutParams mLayoutParams;
	private int mDragCurrentPostion;
	private int mScaledTouchSlop;
	private int mDragOffsetX;
	private int mDragOffSetY;
	private int mDragPointX;
	private int mDragPointY;
	//0:nothing
	//1:dragged
	//2:not dragged
	private int mnDragState;
	private int mnStartX;
	private int mnStartY;
	private int mnWidth;
	private boolean mbOverred;
	private int mnPosition;
	private MHandler mHandler;
	private boolean mbDropping;
	
	private static final int STATE_NONE 		= 0;
	private static final int STATE_DRAGGED_X	= 1;
	private static final int STATE_DRAGGED_Y	= 2;
	
	private ScrollType mScrollType = ScrollType.NONE;

	private OnGestureListener mOnGestureListener;
	private OnItemDoubleClickListener mOnItemDoubleClickListener;
	private OnItemClickListener mOnXItemClickListener = null;
	
	DoubleClick mDoubleClick;
	
	static final String TAG = GestureListView.class.getName();
	
	public static enum ScrollType
	{
		NONE,
		LEFT,
		RIGHT
	};

	public GestureListView(Context context)
	{
		super(context);
		mContext = context;
		initView();
	}

	public GestureListView(Context context, AttributeSet attr)
	{
		super(context, attr);
		mContext = context;
		initView();
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev)
	{
		if(mbDropping)
			return super.onTouchEvent(ev);
		
		switch (ev.getAction())
		{
		case MotionEvent.ACTION_DOWN:
			mnDragState = STATE_NONE;
			mnStartX = (int) ev.getX();
			mnStartY = (int) ev.getY();
			mnWidth = getWidth();
			mScrollType = ScrollType.NONE;
			mbOverred = false;
			if(mDoubleClick.trace())
			{
				doubleClick((int)ev.getX(), (int)ev.getY());
				return true;
			}
			
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			{
				if(mnDragState == STATE_DRAGGED_X)
				{
					int x = (int)ev.getX();
					int y = (int)ev.getY();
					stopDragging(x, y, x - mnStartX, 0);
					mnDragState = STATE_NONE;
					break;
				}
				else if(mnDragState == STATE_DRAGGED_Y)
				{
					
				}
				else
				{
					singleClickX((int)ev.getX(), (int)ev.getY());
				}
				mnDragState = STATE_NONE;
				mnPosition = INVALID_POSITION;
				mnStartX = 0;
				mnStartY = 0;
				break;
			}
		case MotionEvent.ACTION_MOVE:
			{
				if(mnDragState == STATE_DRAGGED_Y)				
					break;
				
				if(mnDragState == STATE_DRAGGED_X)
				{
					int x = (int) ev.getX();
					int y = (int) ev.getY();
					dragView(x, y, x - mnStartX, 0);
					return true;
				}
				else if(mnDragState == STATE_NONE)
				{
					int x = (int) ev.getX();
					int y = (int) ev.getY();
					
					int absX = Math.abs(mnStartX -x);
					int absY = Math.abs(mnStartY - y);
					if(absX > mScaledTouchSlop && absX > absY)
					{
						mnDragState = startDragging(x, y, 
								(int)ev.getRawX(), (int)ev.getRawY()) ? 
								STATE_DRAGGED_X : STATE_NONE;
						;
						return true;
					}
					else if(absY > mScaledTouchSlop && absY > absX)
					{
						mnDragState = STATE_DRAGGED_Y;
						break;
					}
				}
				break;
			}
		}
		return super.onTouchEvent(ev);
	}

	private void doubleClick(int x, int y)
	{
		int position = pointToPosition(x, y);
		if((null != mOnItemDoubleClickListener) && (position != INVALID_POSITION))
		{
			mOnItemDoubleClickListener.onItemDoubleClicked(position);
		}
	}
	
	private synchronized void singleClickX(int x, int y)
	{
		int position = pointToPosition(x, y);
		if((position != -1) && (mOnXItemClickListener != null))
		{
			if(mOnXItemClickListener != null)
				mOnXItemClickListener.onItemClicked(position);
		}
	}
	
	private boolean startDragging(int x, int y, int rawX, int rawY)
	{
		mnPosition = pointToPosition(x, y);
		if (mnPosition == AdapterView.INVALID_POSITION)
		{
			return false;
		}
		Log.d(TAG, "pos ====" + mnPosition);
		final View item = (View) getChildAt(mnPosition	- getFirstVisiblePosition());
		mDragPointX = x - item.getLeft();
		mDragPointY = y - item.getTop();
		mDragOffsetX = ((int) rawX) - x;
		mDragOffSetY = ((int) rawY) - y;
		startDragging(item, mnPosition, x, y);
		return true;
	}
	
	private void startDragging(View item, int itemNum, int x, int y)
	{
		mDragCurrentPostion = itemNum;
		mScrollType = ScrollType.NONE;

		item.setDrawingCacheEnabled(true);
		Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
		startDragging(bitmap, x, y);
		if(null != mOnGestureListener)
			mOnGestureListener.onDragged(mDragCurrentPostion);
	}
	
	private void startDragging(Bitmap bitm, int x, int y)
	{
		stopDragging();

		mLayoutParams = new WindowManager.LayoutParams();
		mLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
		mLayoutParams.x = x - mDragPointX + mDragOffsetX;
		mLayoutParams.y = y - mDragPointY + mDragOffSetY;
		mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
		mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
		mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
				| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
				| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
				| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
				| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
		mLayoutParams.format = PixelFormat.TRANSLUCENT;
		mLayoutParams.windowAnimations = 0;

		ImageView imageView = new ImageView(mContext);
		imageView.setImageBitmap(bitm);
		imageView.setPadding(0, 0, 0, 0);
		
		mDragView = imageView;
		mWindowManager.addView(mDragView, mLayoutParams);
	}
	
	private void stopDragging(int x, int y, int dx, int dy)
	{
		mbDropping = true;
		if(null != mOnGestureListener)
			mOnGestureListener.onOver(mScrollType, false);
		
		if(mScrollType == ScrollType.RIGHT && mbOverred)
		{
			mHandler.sendAnimView(1.0f);
			return;
		}
		stopDragging();
		drop();
	}

	private void stopDragging()
	{
		if (mDragView != null)
		{
			mWindowManager.removeView(mDragView);
			mDragView.setImageDrawable(null);
			mDragView = null;
		}
	}
	
	private void drop()
	{
		if(null != mOnGestureListener)
			mOnGestureListener.onDrop(mScrollType, mbOverred, mnPosition);
		mbDropping = false;
	}
	
	
	private void dragView(int x, int y, int dx, int dy)
	{
		final int DX = mScaledTouchSlop;
		dragView(x, y);
		if(dx > DX)
		{
			if(mScrollType != ScrollType.RIGHT)
			{
				mScrollType = ScrollType.RIGHT;
				if(null != mOnGestureListener)
					mOnGestureListener.onScroll(mScrollType, 0);				
			}
		}
		else if(dx < -DX)
		{
			if(mScrollType != ScrollType.LEFT)
			{
				mScrollType = ScrollType.LEFT;
				if(null != mOnGestureListener)
					mOnGestureListener.onScroll(mScrollType, 0);				
			}
		}
		else if(dx >= -DX && dx <= DX)
		{
			if(mScrollType != ScrollType.NONE)
			{
				mScrollType = ScrollType.NONE;
				if(null != mOnGestureListener)
					mOnGestureListener.onScroll(mScrollType, 0);			
			}
		}
		
		int absDx = Math.abs(dx);
		if(mbOverred && absDx < mnWidth / 2)
		{
			mbOverred = false;
			if(null != mOnGestureListener)
				mOnGestureListener.onOver(mScrollType, mbOverred);
		}
		else if(!mbOverred && absDx >= mnWidth / 2)
		{
			mbOverred = true;
			if(null != mOnGestureListener)
				mOnGestureListener.onOver(mScrollType, mbOverred);
		}
	}

	private void dragView(int x, int y)
	{
		if (mDragView != null)
		{
			mLayoutParams.alpha = mAlpha;
			mLayoutParams.x = x - mDragPointX + mDragOffsetX;
			mWindowManager.updateViewLayout(mDragView, mLayoutParams);
		}
	}
	
	private void animView(float alpha)
	{
		if (mDragView != null)
		{
			mLayoutParams.alpha = alpha;
			mWindowManager.updateViewLayout(mDragView, mLayoutParams);
		}
	}

	public void initView()
	{
		mHandler = new MHandler();
		mbDropping = false;
		mnDragState = STATE_NONE;
		final Context ctx = getContext();
		mWindowManager = (WindowManager) ctx
				.getSystemService(Context.WINDOW_SERVICE);
		mScaledTouchSlop = ViewConfiguration.get(ctx).getScaledTouchSlop();
		mDoubleClick = new DoubleClick();

	}

	public void setOnGestureListener(OnGestureListener listener)
	{
		this.mOnGestureListener = listener;
	}
	
	public void setOnItemDoubleClickListener(OnItemDoubleClickListener l)
	{
		mOnItemDoubleClickListener = l;
	}
	
	public void setOnItemClickedListener(OnItemClickListener l)
	{
		mOnXItemClickListener = l;
	}

	public static interface OnGestureListener
	{
		void onDrop(ScrollType type, boolean bOverred, int position);
		void onDragged(int position);
		void onScroll(ScrollType type, float percent);
		void onOver(ScrollType type, boolean bOverred);
	}
	
	public static interface OnItemDoubleClickListener
	{
		void onItemDoubleClicked(int position);
	}
	
	public static interface OnItemClickListener
	{
		void onItemClicked(int position);
	}
	
	class MHandler extends Handler
	{

		@Override
		public void handleMessage(Message msg)
		{
			if(msg.what == 0)
			{
				Float alpha = (Float)msg.obj;
				if(alpha <= 0.0f)
				{
					stopDragging();
					sendEmptyMessage(1);
				}
				else
				{
					animView(alpha);
					sendAnimView(alpha - 0.1f);
				}
			}
			else if(msg.what == 1)
			{
				drop();
			}
		}
		
		public void sendAnimView(float alpha)
		{
			Message msg = obtainMessage(0, (Float)alpha);
			removeMessages(0);
			sendMessageDelayed(msg, 50);
		}
	}
}
